{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**问题：**\n",
    "假设你正在为一个电影推荐系统设计一个简单的KNN算法。我们有以下一些用户的电影评分数据，数据由两个特征组成：用户对电影A和电影B的评分，分别在1-5之间。用户的标签（电影类型偏好）是动作片（标签0）或者是喜剧片（标签1）。我们有一个新用户，他给电影A评分为3，电影B评分为4。请问这个用户可能偏好哪种类型的电影？\n",
    "\n",
    "**数据：**\n",
    "\n",
    "| 用户   | 电影A评分 | 电影B评分 | 偏好类型 |\n",
    "| ------ | --------- | --------- | -------- |\n",
    "| 用户1  | 5         | 1         | 动作片   |\n",
    "| 用户2  | 4         | 2         | 动作片   |\n",
    "| 用户3  | 2         | 5         | 喜剧片   |\n",
    "| 用户4  | 1         | 4         | 喜剧片   |\n",
    "| 用户5  | 3         | 2         | 动作片   |\n",
    "| 用户6  | 2         | 5         | 喜剧片   |\n",
    "\n",
    "你需要做以下步骤：\n",
    "1. 构造数据\n",
    "2. 创建KNN模型\n",
    "3. 使用数据训练模型\n",
    "4. 预测新用户的喜好"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 0. 引入核心包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.neighbors import KNeighborsClassifier"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. X, y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "X = np.array([[1, 1], [5, 5], [5, 3], [2, 1]])  # 电影评分数据\n",
    "\n",
    "y = np.array([0, 1, 1, 0])  # 0表示动作片，1表示喜剧片"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 2. 创建 KNN 模型\n",
    "k = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "knn = KNeighborsClassifier(n_neighbors=1)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 3. 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>#sk-container-id-1 {\n",
       "  /* Definition of color scheme common for light and dark mode */\n",
       "  --sklearn-color-text: #000;\n",
       "  --sklearn-color-text-muted: #666;\n",
       "  --sklearn-color-line: gray;\n",
       "  /* Definition of color scheme for unfitted estimators */\n",
       "  --sklearn-color-unfitted-level-0: #fff5e6;\n",
       "  --sklearn-color-unfitted-level-1: #f6e4d2;\n",
       "  --sklearn-color-unfitted-level-2: #ffe0b3;\n",
       "  --sklearn-color-unfitted-level-3: chocolate;\n",
       "  /* Definition of color scheme for fitted estimators */\n",
       "  --sklearn-color-fitted-level-0: #f0f8ff;\n",
       "  --sklearn-color-fitted-level-1: #d4ebff;\n",
       "  --sklearn-color-fitted-level-2: #b3dbfd;\n",
       "  --sklearn-color-fitted-level-3: cornflowerblue;\n",
       "\n",
       "  /* Specific color for light theme */\n",
       "  --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n",
       "  --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n",
       "  --sklearn-color-icon: #696969;\n",
       "\n",
       "  @media (prefers-color-scheme: dark) {\n",
       "    /* Redefinition of color scheme for dark theme */\n",
       "    --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n",
       "    --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n",
       "    --sklearn-color-icon: #878787;\n",
       "  }\n",
       "}\n",
       "\n",
       "#sk-container-id-1 {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 pre {\n",
       "  padding: 0;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-hidden--visually {\n",
       "  border: 0;\n",
       "  clip: rect(1px 1px 1px 1px);\n",
       "  clip: rect(1px, 1px, 1px, 1px);\n",
       "  height: 1px;\n",
       "  margin: -1px;\n",
       "  overflow: hidden;\n",
       "  padding: 0;\n",
       "  position: absolute;\n",
       "  width: 1px;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-dashed-wrapped {\n",
       "  border: 1px dashed var(--sklearn-color-line);\n",
       "  margin: 0 0.4em 0.5em 0.4em;\n",
       "  box-sizing: border-box;\n",
       "  padding-bottom: 0.4em;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-container {\n",
       "  /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n",
       "     but bootstrap.min.css set `[hidden] { display: none !important; }`\n",
       "     so we also need the `!important` here to be able to override the\n",
       "     default hidden behavior on the sphinx rendered scikit-learn.org.\n",
       "     See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n",
       "  display: inline-block !important;\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-text-repr-fallback {\n",
       "  display: none;\n",
       "}\n",
       "\n",
       "div.sk-parallel-item,\n",
       "div.sk-serial,\n",
       "div.sk-item {\n",
       "  /* draw centered vertical line to link estimators */\n",
       "  background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n",
       "  background-size: 2px 100%;\n",
       "  background-repeat: no-repeat;\n",
       "  background-position: center center;\n",
       "}\n",
       "\n",
       "/* Parallel-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item::after {\n",
       "  content: \"\";\n",
       "  width: 100%;\n",
       "  border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n",
       "  flex-grow: 1;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel {\n",
       "  display: flex;\n",
       "  align-items: stretch;\n",
       "  justify-content: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  position: relative;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n",
       "  align-self: flex-end;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n",
       "  align-self: flex-start;\n",
       "  width: 50%;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n",
       "  width: 0;\n",
       "}\n",
       "\n",
       "/* Serial-specific style estimator block */\n",
       "\n",
       "#sk-container-id-1 div.sk-serial {\n",
       "  display: flex;\n",
       "  flex-direction: column;\n",
       "  align-items: center;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  padding-right: 1em;\n",
       "  padding-left: 1em;\n",
       "}\n",
       "\n",
       "\n",
       "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n",
       "clickable and can be expanded/collapsed.\n",
       "- Pipeline and ColumnTransformer use this feature and define the default style\n",
       "- Estimators will overwrite some part of the style using the `sk-estimator` class\n",
       "*/\n",
       "\n",
       "/* Pipeline and ColumnTransformer style (default) */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable {\n",
       "  /* Default theme specific background. It is overwritten whether we have a\n",
       "  specific estimator or a Pipeline/ColumnTransformer */\n",
       "  background-color: var(--sklearn-color-background);\n",
       "}\n",
       "\n",
       "/* Toggleable label */\n",
       "#sk-container-id-1 label.sk-toggleable__label {\n",
       "  cursor: pointer;\n",
       "  display: flex;\n",
       "  width: 100%;\n",
       "  margin-bottom: 0;\n",
       "  padding: 0.5em;\n",
       "  box-sizing: border-box;\n",
       "  text-align: center;\n",
       "  align-items: start;\n",
       "  justify-content: space-between;\n",
       "  gap: 0.5em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label .caption {\n",
       "  font-size: 0.6rem;\n",
       "  font-weight: lighter;\n",
       "  color: var(--sklearn-color-text-muted);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n",
       "  /* Arrow on the left of the label */\n",
       "  content: \"▸\";\n",
       "  float: left;\n",
       "  margin-right: 0.25em;\n",
       "  color: var(--sklearn-color-icon);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n",
       "  color: var(--sklearn-color-text);\n",
       "}\n",
       "\n",
       "/* Toggleable content - dropdown */\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content {\n",
       "  display: none;\n",
       "  text-align: left;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content pre {\n",
       "  margin: 0.2em;\n",
       "  border-radius: 0.25em;\n",
       "  color: var(--sklearn-color-text);\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n",
       "  /* Expand drop-down */\n",
       "  display: block;\n",
       "  width: 100%;\n",
       "  overflow: visible;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n",
       "  content: \"▾\";\n",
       "}\n",
       "\n",
       "/* Pipeline/ColumnTransformer-specific style */\n",
       "\n",
       "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator-specific style */\n",
       "\n",
       "/* Colorize estimator box */\n",
       "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  /* The background is the default theme color */\n",
       "  color: var(--sklearn-color-text-on-default-background);\n",
       "}\n",
       "\n",
       "/* On hover, darken the color of the background */\n",
       "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "/* Label box, darken color on hover, fitted */\n",
       "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n",
       "  color: var(--sklearn-color-text);\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Estimator label */\n",
       "\n",
       "#sk-container-id-1 div.sk-label label {\n",
       "  font-family: monospace;\n",
       "  font-weight: bold;\n",
       "  display: inline-block;\n",
       "  line-height: 1.2em;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-label-container {\n",
       "  text-align: center;\n",
       "}\n",
       "\n",
       "/* Estimator-specific */\n",
       "#sk-container-id-1 div.sk-estimator {\n",
       "  font-family: monospace;\n",
       "  border: 1px dotted var(--sklearn-color-border-box);\n",
       "  border-radius: 0.25em;\n",
       "  box-sizing: border-box;\n",
       "  margin-bottom: 0.5em;\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-0);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-0);\n",
       "}\n",
       "\n",
       "/* on hover */\n",
       "#sk-container-id-1 div.sk-estimator:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-2);\n",
       "}\n",
       "\n",
       "#sk-container-id-1 div.sk-estimator.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-2);\n",
       "}\n",
       "\n",
       "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n",
       "\n",
       "/* Common style for \"i\" and \"?\" */\n",
       "\n",
       ".sk-estimator-doc-link,\n",
       "a:link.sk-estimator-doc-link,\n",
       "a:visited.sk-estimator-doc-link {\n",
       "  float: right;\n",
       "  font-size: smaller;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1em;\n",
       "  height: 1em;\n",
       "  width: 1em;\n",
       "  text-decoration: none !important;\n",
       "  margin-left: 0.5em;\n",
       "  text-align: center;\n",
       "  /* unfitted */\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted,\n",
       "a:link.sk-estimator-doc-link.fitted,\n",
       "a:visited.sk-estimator-doc-link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n",
       ".sk-estimator-doc-link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover,\n",
       "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n",
       ".sk-estimator-doc-link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "/* Span, style for the box shown on hovering the info icon */\n",
       ".sk-estimator-doc-link span {\n",
       "  display: none;\n",
       "  z-index: 9999;\n",
       "  position: relative;\n",
       "  font-weight: normal;\n",
       "  right: .2ex;\n",
       "  padding: .5ex;\n",
       "  margin: .5ex;\n",
       "  width: min-content;\n",
       "  min-width: 20ex;\n",
       "  max-width: 50ex;\n",
       "  color: var(--sklearn-color-text);\n",
       "  box-shadow: 2pt 2pt 4pt #999;\n",
       "  /* unfitted */\n",
       "  background: var(--sklearn-color-unfitted-level-0);\n",
       "  border: .5pt solid var(--sklearn-color-unfitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link.fitted span {\n",
       "  /* fitted */\n",
       "  background: var(--sklearn-color-fitted-level-0);\n",
       "  border: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".sk-estimator-doc-link:hover span {\n",
       "  display: block;\n",
       "}\n",
       "\n",
       "/* \"?\"-specific style due to the `<a>` HTML tag */\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link {\n",
       "  float: right;\n",
       "  font-size: 1rem;\n",
       "  line-height: 1em;\n",
       "  font-family: monospace;\n",
       "  background-color: var(--sklearn-color-background);\n",
       "  border-radius: 1rem;\n",
       "  height: 1rem;\n",
       "  width: 1rem;\n",
       "  text-decoration: none;\n",
       "  /* unfitted */\n",
       "  color: var(--sklearn-color-unfitted-level-1);\n",
       "  border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted {\n",
       "  /* fitted */\n",
       "  border: var(--sklearn-color-fitted-level-1) 1pt solid;\n",
       "  color: var(--sklearn-color-fitted-level-1);\n",
       "}\n",
       "\n",
       "/* On hover */\n",
       "#sk-container-id-1 a.estimator_doc_link:hover {\n",
       "  /* unfitted */\n",
       "  background-color: var(--sklearn-color-unfitted-level-3);\n",
       "  color: var(--sklearn-color-background);\n",
       "  text-decoration: none;\n",
       "}\n",
       "\n",
       "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n",
       "  /* fitted */\n",
       "  background-color: var(--sklearn-color-fitted-level-3);\n",
       "}\n",
       "\n",
       ".estimator-table summary {\n",
       "    padding: .5rem;\n",
       "    font-family: monospace;\n",
       "    cursor: pointer;\n",
       "}\n",
       "\n",
       ".estimator-table details[open] {\n",
       "    padding-left: 0.1rem;\n",
       "    padding-right: 0.1rem;\n",
       "    padding-bottom: 0.3rem;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table {\n",
       "    margin-left: auto !important;\n",
       "    margin-right: auto !important;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:nth-child(odd) {\n",
       "    background-color: #fff;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:nth-child(even) {\n",
       "    background-color: #f6f6f6;\n",
       "}\n",
       "\n",
       ".estimator-table .parameters-table tr:hover {\n",
       "    background-color: #e0e0e0;\n",
       "}\n",
       "\n",
       ".estimator-table table td {\n",
       "    border: 1px solid rgba(106, 105, 104, 0.232);\n",
       "}\n",
       "\n",
       ".user-set td {\n",
       "    color:rgb(255, 94, 0);\n",
       "    text-align: left;\n",
       "}\n",
       "\n",
       ".user-set td.value pre {\n",
       "    color:rgb(255, 94, 0) !important;\n",
       "    background-color: transparent !important;\n",
       "}\n",
       "\n",
       ".default td {\n",
       "    color: black;\n",
       "    text-align: left;\n",
       "}\n",
       "\n",
       ".user-set td i,\n",
       ".default td i {\n",
       "    color: black;\n",
       "}\n",
       "\n",
       ".copy-paste-icon {\n",
       "    background-image: url();\n",
       "    background-repeat: no-repeat;\n",
       "    background-size: 14px 14px;\n",
       "    background-position: 0;\n",
       "    display: inline-block;\n",
       "    width: 14px;\n",
       "    height: 14px;\n",
       "    cursor: pointer;\n",
       "}\n",
       "</style><body><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>KNeighborsClassifier(n_neighbors=1)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow\"><div><div>KNeighborsClassifier</div></div><div><a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.7/modules/generated/sklearn.neighbors.KNeighborsClassifier.html\">?<span>Documentation for KNeighborsClassifier</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></div></label><div class=\"sk-toggleable__content fitted\" data-param-prefix=\"\">\n",
       "        <div class=\"estimator-table\">\n",
       "            <details>\n",
       "                <summary>Parameters</summary>\n",
       "                <table class=\"parameters-table\">\n",
       "                  <tbody>\n",
       "                    \n",
       "        <tr class=\"user-set\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('n_neighbors',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">n_neighbors&nbsp;</td>\n",
       "            <td class=\"value\">1</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('weights',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">weights&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;uniform&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('algorithm',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">algorithm&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;auto&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('leaf_size',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">leaf_size&nbsp;</td>\n",
       "            <td class=\"value\">30</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('p',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">p&nbsp;</td>\n",
       "            <td class=\"value\">2</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('metric',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">metric&nbsp;</td>\n",
       "            <td class=\"value\">&#x27;minkowski&#x27;</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('metric_params',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">metric_params&nbsp;</td>\n",
       "            <td class=\"value\">None</td>\n",
       "        </tr>\n",
       "    \n",
       "\n",
       "        <tr class=\"default\">\n",
       "            <td><i class=\"copy-paste-icon\"\n",
       "                 onclick=\"copyToClipboard('n_jobs',\n",
       "                          this.parentElement.nextElementSibling)\"\n",
       "            ></i></td>\n",
       "            <td class=\"param\">n_jobs&nbsp;</td>\n",
       "            <td class=\"value\">None</td>\n",
       "        </tr>\n",
       "    \n",
       "                  </tbody>\n",
       "                </table>\n",
       "            </details>\n",
       "        </div>\n",
       "    </div></div></div></div></div><script>function copyToClipboard(text, element) {\n",
       "    // Get the parameter prefix from the closest toggleable content\n",
       "    const toggleableContent = element.closest('.sk-toggleable__content');\n",
       "    const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';\n",
       "    const fullParamName = paramPrefix ? `${paramPrefix}${text}` : text;\n",
       "\n",
       "    const originalStyle = element.style;\n",
       "    const computedStyle = window.getComputedStyle(element);\n",
       "    const originalWidth = computedStyle.width;\n",
       "    const originalHTML = element.innerHTML.replace('Copied!', '');\n",
       "\n",
       "    navigator.clipboard.writeText(fullParamName)\n",
       "        .then(() => {\n",
       "            element.style.width = originalWidth;\n",
       "            element.style.color = 'green';\n",
       "            element.innerHTML = \"Copied!\";\n",
       "\n",
       "            setTimeout(() => {\n",
       "                element.innerHTML = originalHTML;\n",
       "                element.style = originalStyle;\n",
       "            }, 2000);\n",
       "        })\n",
       "        .catch(err => {\n",
       "            console.error('Failed to copy:', err);\n",
       "            element.style.color = 'red';\n",
       "            element.innerHTML = \"Failed!\";\n",
       "            setTimeout(() => {\n",
       "                element.innerHTML = originalHTML;\n",
       "                element.style = originalStyle;\n",
       "            }, 2000);\n",
       "        });\n",
       "    return false;\n",
       "}\n",
       "\n",
       "document.querySelectorAll('.fa-regular.fa-copy').forEach(function(element) {\n",
       "    const toggleableContent = element.closest('.sk-toggleable__content');\n",
       "    const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : '';\n",
       "    const paramName = element.parentElement.nextElementSibling.textContent.trim();\n",
       "    const fullParamName = paramPrefix ? `${paramPrefix}${paramName}` : paramName;\n",
       "\n",
       "    element.setAttribute('title', fullParamName);\n",
       "});\n",
       "</script></body>"
      ],
      "text/plain": [
       "KNeighborsClassifier(n_neighbors=1)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "knn.fit(X, y)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 4. 用模型推理(预测)用户的喜好"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_user = np.array([[3, 4]])\n",
    "prediction = knn.predict(new_user)[0]"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5. 数据可视化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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1vTanPTC8axrWlLL00GzVnLNWERERJT9OB3cDB48WDUbPfXUU1K8ePEMdcUePXq0dOnSRcqUKSNhYWEqD2bz5s2ybt06tb1///5SsmRJmTRpkno8YcIEue+++1RNz507d1S39PPnz6tzIiIiIkpXcINB9latWiUtWrTI8DuImh8EMFevXlVj5aCZC4EN2ukAuTg+PvfGGbx9+7Y8++yzcu3aNcmfP79qxtqxYwdHQiYiIqL0BzcIKjJr3qivv/46xe2oxbGG3lhYiIiIiDJt+gX0SsIIxOiSRUREROT1NTcYvO/06dNStGhRKVeuXJLsZUyqSUREROQ1wY0+CjERERGRIYKbsWPHuuZMiIiIiNyRc0NERETk9TU36B31999/q2GP0VsqpbFt/v3338w8PyIiIqLMD27Q/Tp37tyW3zMycB8RERGR24MbzPite/rpp115PkRERERZm3Pj6+urRha2d+vWLbWNiIiIyKuCG03THK7HZJeY4ZuIiIjIK7qCf/rpp+on8m3mzJkjuXLlspmde+vWrVKtWjXXnCURERFRZgc3+pxOqLmZNWuWTRMUamwwWjHWExEREXlFcHP27Fn1s02bNvLzzz+rLuFEREREXj9C8aZNm1xzJkRERETuCG7g0qVLsmLFCrlw4YLExsbabPv4448z47yIiIiIsia42bBhg/To0UMqVKggf/31l9SqVUvOnTuncnEaNGiQvrMgIiIicldX8NGjR8trr70mhw8flqCgIPnpp5/k4sWL0qpVK+nTp09mnRcRERFR1gQ3x48fl/79+6vf/fz8JCoqSnULnzBhgkyePDl9Z0FERETkruAmZ86cljyb4sWLy+nTpy3bbt68mVnnRURERJQ1OTf33XefbNu2TapXry5du3aVESNGqCYqdA/HNiIiIiKvCm7QGyo8PFz9Pn78ePX7jz/+KJUrV2ZPKSIiIvKu4AbTLKAbeJ06dSxNVByVmIiIiLw25wZTLnTs2FFu377tujMiIiIiysqEYoxrc+bMmYy8JhEREZHnBDfvvvuuGudm5cqVcvXqVbl7967NQkRERORVCcXoIQUYpdhkMlnWY4RiPEZeDhEREZG7cOJMIiIiyt7BDaZZICIiItJpWrxI3EGRuEOSGBspIqVFi14tmk9LMfkWFK+YFZyIiIhI0zSRmPWiRa0QSTgrosWJxPuLyLOi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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.title(\"User Movie Preference\", size=15) \n",
    "plt.xlabel(\"Movie A rating\")\n",
    "plt.ylabel(\"Movie B rating\")\n",
    "plt.grid()\n",
    "\n",
    "# 绘制原始样本点，要求不同的影片喜好类别用不同的颜色标记\n",
    "# 提示: 用scatter散点图绘制，用它的参数c实现不同的类别用不同的颜色标记\n",
    "plt.scatter(X[:, 0], X[:, 1], c=y, cmap='viridis', s=100, alpha=0.7, label='Training Data')\n",
    "\n",
    "# 绘制新数据点，用红色x标记，大小为8\n",
    "# 提示：用plt.plot()绘制，用它的参数marker实现不同的标记符号\n",
    "plt.plot(new_user[0, 0], new_user[0, 1], 'rx', markersize=8, label='New User')\n",
    "\n",
    "# 新数据最近邻索引为第一个最近邻的索引\n",
    "dist, idx = knn.kneighbors(new_user)\n",
    "nearest = X[idx[0][0]] # 获取最近邻点的坐标，这是一个列表，第一个元素是x坐标，第二个元素是y坐标\n",
    "\n",
    "# 用红线标记新数据点与最近邻点的连接线\n",
    "# 提示：用plt.plot()绘制，用 r-- 实现红色虚线\n",
    "plt.plot([new_user[0, 0], nearest[0]], [new_user[0, 1], nearest[1]], 'r--', linewidth=2, label='Nearest Neighbor')\n",
    "\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "movie-recommendation-app (3.11.8)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.8"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
